/**
=blacksmith/term - A terminal interface

The term module provides a command-line interface for interacting with the system.

*/


window.TERM = {}



TERM.Term = function(container) {
    /**
     *=Term - Term object
     *
     */
    
    var self = {},
        COMMANDS = {
         
        'cd': {
            'help': "cd 'path'      - Change the current working directory",
            'do': function (args) {
                self.print_style = 'term_files'
                BLACKSMITH.OS.chdir(args[1], function() {
                    self.read('$cwd#', self.execute)
                })
                self.setprompt( '' )
            }
        },
        
        'cp': {
            'help': "cp 'src' 'dst' - Copy a file",
            'do': function (args) {
                args.shift()
                args[0] = BLACKSMITH.OS.PATH.abspath(args[0])
                args[1] = BLACKSMITH.OS.PATH.abspath(args[1])
                print('['+args+']')
                BLACKSMITH.OS.concat( args, false, self.response)
                self.setprompt('')
            }
        },
        
        'help': {
            'help': "help           - List available commands",
            'do': function (args) {
                Object.keys(COMMANDS).forEach(function (cmd) {
                    self.print(COMMANDS[cmd]['help'])
                })
                self.response()
            }
        },
        
        'id': {
            'help': "id             - Get user identity",
            'do': function (args) {
                self.print(BLACKSMITH.environ.user.name)
                self.response()
            }
        },
        
        'logout': {
            'help': "logout         - Log current user out",
            'do': function (args) {
                BLACKSMITH.OS.logout(document.location, function(m) {
                    document.location = m.logout
                });
            }
        },
        
        'ls': {
            'help': "ls ['path']    - List files",
            'do': function (args) {
                var dir = '-'
                
                args.shift()
                
                while (dir.charAt(0) === '-' && args.length > 0)
                    dir = args.shift()
                
                if (dir.charAt(0) === '-')
                    dir = '.'
                
                self.print_style = 'term_files'
                BLACKSMITH.OS.listdir(dir, self.response)
                self.setprompt('')
            }
        },
        
        'mkdir': {
            'help': "mkdir 'path'   - Make a directory",
            'do': function (args) {
                BLACKSMITH.OS.mkdir( args[ 1 ], self.response )
                self.setprompt('')
            }
        },
        
        'mv': {
            'help': "mv 'src' 'dst' - Move file or directory",
            'do': function (args) {
                BLACKSMITH.OS.move(args[1], args[2], self.response)
                self.setprompt('')
            }
        },
        
        'echo': {
            'help': "echo           - Send to the terminal",
            'do': function (args) {
                self.print(args[1])
                self.response()
            }
        },
        
        'rm': {
            'help': "rm 'path'      - Remove file",
            'do': function (args) {
                BLACKSMITH.OS.remove(args[1], self.response)
                self.setprompt('')
            }
        },
        
        'rmdir': {
            'help': "rmdir 'path'   - Remove directory",
            'do': function (args) {
                BLACKSMITH.OS.rmdir(args[1], self.response)
                self.setprompt('')
            }
        },
        
        'time': {
            'help': "time           - Get current local time",
            'do': function (args) {
                self.print((new Date()).toLocaleString())
                self.response()
            }
        },
        
        'version': {
            'help': "version        - Get blacksmith version",
            'do': function (args) {
                self.print(BLACKSMITH.environ.version)
                self.response()
            }
        }
        
        }

    
    
    self.print = function(output) {
        /**
         *>print(output)
         *
         * Writes output to the history buffer.
         *
         */
        
        if (self.buffer.length === BLACKSMITH.environ.term_history)
            self.history.removeChild(self.buffer.shift())
        
        if (!(output instanceof HTMLElement)) {
            if (typeof output === 'undefined') {
                output = '';
            } else if (typeof output === 'object') {
                if (output.hasOwnProperty('error'))
                    self.print_style = 'term_error'
                output = JSON.stringify(output)
            }
            output = output.replace(/", "|","|"]|"}/g, '\n')
            output = output.replace(/\["|{"|\[\]/g, '')
            output = output.replace(/":"|": "/g, ': ')
            output = output.replace(/\\\//g, '/')
        }
        
        output = BLACKSMITH.element(self.history, 'div', 'term_print ' + self.print_style, [output])
        self.buffer.push(output)
        self.cmd.scrollIntoView()
        
        return output

    }
    
    
    self.read = function(prompt, fn) {
        /**
         *>read('prompt', callback)
         *
         * Read input from the user.
         *
         */
        
        self.setprompt(prompt)
        self.cmd.focus()
        self.callback = fn
        
        return self

    }

    
    
    self.setprompt = function(prompt) {
        /**
         *>setprompt('prompt')
         *
         * Updates the prompt component value.
         *
         */
        
        for (var prop in BLACKSMITH.environ)
            prompt = prompt.replace('$'+prop, BLACKSMITH.environ[prop])
        
        self.prm.textContent = prompt
        BLACKSMITH.environ.prompt = prompt
        self.layout()
        
        return self

    }

    
    
    self.response = function(response) {
        /**
         *>response(response)
         *
         * Prints the response and prompts for input.
         *
         */
        
        self.print(response)
        self.read('$cwd#', self.execute)
        
    }
    
    
    
    self.execute = function(input) {
        /**
         *>execute('input')
         *
         * Executes a pipe separated string of commands.
         *
         */
        
        var cmd = input.replace(/ +/g, ' ').split(' ')
        
        self.print_style = ''
        
        if (cmd[0] in COMMANDS) {
            
            COMMANDS[cmd[0]]['do'](cmd)
            return self
            
        }
        
        var abspath = BLACKSMITH.OS.PATH.abspath(cmd[0]),
            dir = BLACKSMITH.OS.PATH.dirname(abspath),
            file = BLACKSMITH.OS.PATH.basename(cmd[0]),
            checked = 0
            
        file.endsWith('.js') || (file = file + '.js')
        
        BLACKSMITH.OS.listdir(dir, function(files) {
            
            if (files.indexOf(file) > -1) {
                
                COMMANDS[cmd[0]] = {
                    'help': '{0:14} - {1}'.format(cmd[0], abspath),
                    'do': function (args) {
                        window.open((args.length > 1) ? abspath + '?f=' + BLACKSMITH.OS.PATH.abspath(args[1]) : abspath)
                        self.response()
                    }
                }
                
                self.pending = function() {COMMANDS[cmd[0]]['do'](cmd)}
                return
                
            }
            
            BLACKSMITH.environ.path.forEach(function(path) {
                
                BLACKSMITH.OS.listdir(path, function(files) {
                    
                    if (files.indexOf(file) > -1) {
                        
                        abspath = BLACKSMITH.OS.PATH.abspath(path + '/' + BLACKSMITH.OS.PATH.basename(cmd[0]))
                        
                        COMMANDS[cmd[0]] = {
                            'help': '{0:14} - {1}'.format(BLACKSMITH.OS.PATH.basename(cmd[0]), abspath),
                            'do': function (args) {
                                window.open((args.length > 1) ? abspath + '?f=' + BLACKSMITH.OS.PATH.abspath(args[1]) : abspath)
                                self.response()
                            }
                        }
                         
                        self.pending = function() {COMMANDS[cmd[0]]['do'](cmd)}
                        return
                        
                    }
                    
                    if (++checked === BLACKSMITH.environ.path.length)
                        self.print('Command not found "' + cmd[0] + '"')
                    
                })
            
            })
            
        })
        
        self.response()
        return self
        
    }
    
    
    
    self.layout = function() {
        /**
         *>layout()
         *
         * Updates the layout of the display.
         *
         */
        
        self.cmd.style.width = (self.panel.offsetWidth - self.prm.offsetWidth - 30) + 'px'
        
        return self
    }
    
    
    
    self.run = function() {
        
        requestAnimationFrame(self.run)
        
        if (self.pending) {
            
            self.pending()
            self.pending = null
            
        }
        
    }
    
    
    self.container = container
    
    self.container.style['background-color'] = '#111111'
    
    self.buffer   = []
    self.callback = null
    self.panel    = BLACKSMITH.element(self.container, 'div', 'term_container')
    self.history  = BLACKSMITH.element(self.panel, 'div', 'term_history')
    self.prm      = BLACKSMITH.element(self.panel, 'span', 'term_prompt')
    
    self.print_style            = ''
    self.prm.style.paddingRight = '2px'
    
    self.cmd         = BLACKSMITH.element(self.panel, 'input', 'term_cmdline', {'type': 'text'})
    self.cmd.onfocus = function() {self.cmd.style.outline='none'}
        
    self.panel.onresize = self.layout
    self.panel.onclick  = self.focus
    
    self.previous_commands = []
    self.previous_index = 0
    
    
    document.onclick = function() {
        self.cmd.focus()
    }
    
    
    
    self.cmd.onkeydown = function(event) {
        
        if (event.keyCode === 38) {
            if (--self.previous_index < 0)
                self.previous_index = (self.previous_commands.length === 0) ? 0 : self.previous_commands.length - 1
            self.cmd.value = (self.previous_index < self.previous_commands.length) ? self.previous_commands[self.previous_index] : ''
        } else if (event.keyCode === 40) {
            if (++self.previous_index > self.previous_commands.length)
                self.previous_index = 0
            self.cmd.value = (self.previous_index < self.previous_commands.length) ? self.previous_commands[self.previous_index] : ''
        }
        
        if (event.keyCode != '13')
            return true
            
        var p = self.prm.cloneNode(true),
            input = self.cmd.value
        
        p.style.marginRight = '0px'
        self.cmd.value = ''
        
        self.print(BLACKSMITH.element('span', '', [ p,
            BLACKSMITH.element('span', 'term_cmdline', [input])
        ]))
            
        if (input.length > 0) {
            self.previous_commands.push(input)
            self.previous_index = self.previous_commands.length
            if (self.callback) {
                var fn = self.callback
                self.callback = null
                fn(input)
            }
        }
        
    }
        
    BLACKSMITH.environ.term_history = 100
    
    self.run()
    
    return self
}


BLACKSMITH.element(document.head, 'style', {'type': 'text/css'}, [
    '.term_container {',
    '  color: #cccccc;',
    '  border: 0;',
    '  margin: 0;',
    '  padding: 5px;',
    '  font: normal normal normal 16pt monospace;',
    '  white-space: pre;',
    '}',
    '.term_history {',
    '}',
    '.term_prompt {',
    '  color: white;',
    '  border: 0;',
    '}',
    '.term_cmdline {',
    '  color: #cccccc;',
    '  background: none;',
    '  font: normal normal normal 16pt monospace;',
    '  white-space: pre;',
    '  border: 0;',
    '}',
    '.term_print {',
    '  white-space: pre;',
    '  padding-top: 3px;',
    '  border: 0;',
    '}',
    '.term_files {',
    '  color: #00ff00;',
    '}',
    '.term_error {',
    '  color: #ff0000;',
    '}'
])


if (BLACKSMITH.__main__ === 'blacksmith/term') {
    /**
     *=Main application
     *
     * When run as the main application this module will automatically
     * create a Term instance using document.body as the container.
     *
     * Available commands
     *
     *./# help
     *.cd 'path'      - Change the current working directory
     *.cp 'src' 'dst' - Copy a file
     *.help           - List available commands
     *.id             - Get user identity
     *.logout         - Log current user out
     *.ls ['path']    - List files
     *.mkdir 'path'   - Make a directory
     *.mv 'src' 'dst' - Move file or directory
     *.echo           - Send to the terminal
     *.rm 'path'      - Remove file
     *.rmdir 'path'   - Remove directory
     *.time           - Get current local time
     *.version        - Get blacksmith version 
     *
     */
    
    BLACKSMITH.OS.identify(document.location, function(user) {
        
        var sh = TERM.Term(document.body),
            p = '/'
        
        sh.print('Welcome ' + BLACKSMITH.environ.user.name)
        sh.print((new Date()).toLocaleString())
        sh.print(BLACKSMITH.environ.version)
        sh.print()
        
        BLACKSMITH.argv.forEach(function(arg) {
          arg[0] === 'p' && (p = arg[1])
        })

        BLACKSMITH.OS.chdir(p, function() {
            sh.read('$cwd#', sh.execute)
        })
        
        
    })
    
    BLACKSMITH.element(document.head, 'syle', {'type': 'text/css'}, [
        'html {',
        '  height: 100%;',
        '}',
        'body {',
        '  height: 100%;',
        '}'
    ])
}

